#ifndef _VECTOR_
#define _VECTOR_

#include "Point.h"
#include <iostream>
#include <math.h>
#include <vector>

/**
 * @brief Vector<DIM>向量类 
 * 
 */
template <unsigned int DIM>
class Vector
{
protected:
    Point<DIM> p_begin;
    Point<DIM> p_end;
public:
    Vector(){};
    /**
     * 
     * @brief  构造函数:单点
     * 
     * @param  p2:终点
     * 
     */
    Vector(const Point<DIM>& p2);
    /**
     * 
     * @brief  构造函数:两点
     * 
     * @param  p1:起点
     * @param  p2:终点
     * 
     */
    Vector(const Point<DIM>& p1, const Point<DIM>& p2);
    ~Vector(){};
    /**
     * 
     * @brief  复制构造函数
     * 
     * @param  v
     * 
     */
    Vector(const Vector<DIM>& v);
    /**
     * 
     * @brief  =左值重载
     * 
     * @param  v
     * 
     */
    Vector& operator = (const Vector<DIM>& v);
    /**
     * 
     * @brief  =右值重载
     * 
     * @param  v
     * 
     */
    Vector& operator = (Vector<DIM>&& v);
    /**
     * 
     * @brief  获取向量坐标形式  
     * 
     * @return  坐标向量
     * 
     */
    const vector<double> getValue() const;
    /**
     * 
     * @brief  获取第d个维度分量
     * 
     * @param  d
     * 
     * @return  坐标分量
     * 
     */
    const double getValue(const unsigned int d) const;
    /**
     * 
     * @brief  从向量得到点
     * 
     * @param  i:0为起点，1为终点 
     * 
     * @return  点类
     * 
     */
    const Point<DIM> getPoint(const unsigned int i) const;
    /**
     * 
     * @brief  向量模长
     * 
     * @param  i:范数
     * 
     * @return  模长
     * 
     */
    const double norm(const unsigned int i) const;
    /**
     * 
     * @brief  []重载
     * 
     * @param  d
     * 
     * @return  向量分量
     * 
     */
    double operator [] (const unsigned int d);//与point的不同，因为point有成员value因此地址存在，而临时变量地址不存在。
    const double operator [](const unsigned int d) const;
    /**
     * 
     * @brief  向量取负
     * 
     */
    Vector<DIM>& operator - ();
    /**
     * 
     * @brief  向量加法
     * 
     * @param  v1
     * @param  v2
     * 
     */
    template <unsigned int D>
    friend Vector<D> operator + (const Vector<D>& v1, const Vector<D>& v2);
    /**
     * 
     * @brief  向量减法
     * 
     * @param  v1
     * @param  v2
     * 
     */
    template <unsigned int D>
    friend Vector<D> operator - (const Vector<D>& v1, const Vector<D>& v2);
    /**
     * 
     * @brief  向量內积
     * 
     * @param  v1
     * @param  v2
     * 
     */
    template <unsigned int D>
    friend double operator * (const Vector<D>& v1, const Vector<D>& v2);
    /**
     * 
     * @brief  向量外积（仅3维）
     * 
     * @param  v1
     * @param  v2
     * 
     */
    template <unsigned int D>
    friend Vector<D> operator ^ (const Vector<D>& v1, const Vector<D>& v2);
    /**
     * 
     * @brief  向量输出流重载
     * 
     * @param  os
     * @param  v
     * 
     */
    template <unsigned int D>
    friend ostream& operator<<(ostream& os, const Vector<D>& v);
};

template <unsigned int DIM>
Vector<DIM>::Vector(const Vector<DIM>& v)
{
    p_begin = v.p_begin;
    p_end = v.p_end;
}

template <unsigned int DIM>
Vector<DIM>::Vector(const Point<DIM>& p2)
{
    Point<DIM> p1;
    p_begin = p1;
    p_end = p2;
}

template <unsigned int DIM>
Vector<DIM>::Vector(const Point<DIM>& p1, const Point<DIM>& p2)
{
    p_begin = p1;
    p_end = p2;
}

template <unsigned int DIM>
const vector<double> Vector<DIM>::getValue() const
{
    vector<double> tmp(DIM);
    for(unsigned int d = 0; d < DIM; d++)
    {
	tmp[d] = p_end[d] - p_begin[d];
    }
    return tmp;
}

template <unsigned int DIM>
const double Vector<DIM>::getValue(const unsigned int d) const
{
    return p_end[d] - p_begin[d];
}

template <unsigned int DIM>
const Point<DIM> Vector<DIM>::getPoint(const unsigned int i) const
{
    switch(i)
    {
    case 0: return p_begin;break;
    case 1: return p_end;break;
    default: abort(1);
    }
};

template <unsigned int DIM>
const double Vector<DIM>::norm(const unsigned int i) const
{
    if(i == 1)
    {
	double s = 0;
	for(int d = 0; d < DIM; d++)
	    s += fabs(p_begin[d] - p_end[d]);
	return s;
    }
    else if(i == 2)
	return getDistance(p_begin, p_end);
    else
	abort(2);
};

template <unsigned int DIM>
double Vector<DIM>:: operator [] (const unsigned int d)
{
    return p_end[d] - p_begin[d];
}

template <unsigned int DIM>
const double Vector<DIM>:: operator [] (const unsigned int d) const
{
    return p_end[d] - p_begin[d];
}

template <unsigned int DIM>
Vector<DIM>& Vector<DIM>::operator - ()
{
    Point<DIM> p;
    p = p_begin;
    this->p_begin = p_end;
    this->p_end = p;
    return *this;
}

template <unsigned int DIM>
Vector<DIM>& Vector<DIM>::operator = (const Vector<DIM>& v)
{
    this->p_begin = v.p_begin;
    return *this;
}

template <unsigned int D>
Vector<D> operator + (const Vector<D>& v1, const Vector<D>& v2)
{
    Vector<D> v;
    v.p_begin = v1.p_begin;
    for(int d = 0; d < D; d++)
    	v.p_end[d] = v1.p_end[d] + v2.p_end[d] - v2.p_begin[d];
    return v;
}

template <unsigned int D>
Vector<D> operator - (const Vector<D>& v1, const Vector<D>& v2)
{
    Vector<D> v;
    v.p_begin = v1.p_begin;
    for(int d = 0; d < D; d++)
    	v.p_end[d] = v1.p_end[d] + v2.p_begin[d] - v2.p_end[d];
    return v;
}

template <unsigned int D>
double operator * (const Vector<D>& v1, const Vector<D>& v2)
{
    double s = 0;
    for(int d = 0; d < D; d++)
	s += v1[d] * v2[d];
	//s+=v1.getValue(d) * v2.getValue(d);//调用需要函数有const，否则认为可修改而报错
    return s;
}

template <unsigned int D>
Vector<D> operator ^ (const Vector<D>& v1, const Vector<D>& v2)
{
    if(D == 3)
    {
	Vector<D> v;
	vector<double> tmp = {v1[1] * v2[2] - v2[1] * v1[2] + v1.p_begin[0], v1[2] * v2[0] - v2[2] * v1[0] + v1.p_begin[1], v1[0] * v2[1] - v2[0] * v1[1] + v1.p_begin[2]};
        v.p_begin = v1.p_begin;
	Point<D> p(tmp);
	v.p_end = p;
        return v;
    }	
    else
	abort(3);
}

template <unsigned int D>
ostream& operator<<(ostream& os, const Vector<D>& v)
{
    os << "(";
    for(unsigned int d = 0; d < D - 1; d++)
    {
	os << v.p_begin[d]<< ", ";
    }
    os << v.p_begin[D - 1]<< ")";
    os << " -> (";
    for(unsigned int d = 0; d < D - 1; d++)
    {
	os << v.p_end[d]<< ", ";
    }
    os << v.p_end[D - 1]<< ")";
    return os;
}

#else
#endif
